/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_TriComplex_h__
#define __dylab_TriComplex_h__

//===========================================================================================
#include <dylab/utils/geometry/TriangleGeometry3.h>
#include <dylab/utils/topology/EdgeComplex.h>

//===========================================================================================
namespace dylab {
            
    /** TriComplex is 2-simplical _complex.
     */   
    template <
        class _NodeSimplexDataT = EmptyClass,
        typename _EdgeSimplexDataT = EmptyClass,
        typename _TriSimplexDataT = EmptyClass
    >
    class TriComplex 
        : public EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>
    {           
    public:
        // only for convenience (and typing ;-)
        typedef typename NodeComplex<_NodeSimplexDataT> NodeComplexT;
        typedef typename EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT> EdgeComplexT;
        typedef typename TriComplex<_NodeSimplexDataT, _EdgeSimplexDataT, _TriSimplexDataT> TriComplexT;
    public:
        //-----------------------------------------------------------------------------
        /// TriSimplex is 2-_simplex.
        class TriSimplex 
            : public Simplex
            , public _TriSimplexDataT
        {      
        public:
            //
            static const order_t ORDER = ORDER_2;
        private:
            ///
            nodeSimplexID_t mNodeIDs[TriangleGeometry3::V_COUNT];
        public:
            /**
             */
            TriSimplex(TriComplexT * _complex, nodeSimplexID_t _n1, nodeSimplexID_t _n2, nodeSimplexID_t _n3)
                : Simplex(ORDER, _complex->generateTriID(_n1, _n2, _n3), _complex, true, false)
                    { 
                        // store node ids
                        mNodeIDs[TriangleGeometry3::V_0] = _n1;
                        mNodeIDs[TriangleGeometry3::V_1] = _n2;
                        mNodeIDs[TriangleGeometry3::V_2] = _n3;

                        // add all triangle edges, if not added yet
                        _attachEdge(_n1, _n2);
                        _attachEdge(_n2, _n3);
                        _attachEdge(_n3, _n1);
                            
                        // notify that the triangle creation is done
                        getTriComplex()->_notifySimplexCreated(this); 
                    }
            /**
             */
            virtual ~TriSimplex()
                    { }
            /** 
             */
            TriComplexT * getTriComplex()
                    { return dynamicCast<TriComplexT>(getComplex()); }
            /** 
             */
            const TriComplexT * getTriComplex() const
                    { return dynamicCast<const TriComplexT>(getComplex()); }
            /** 
             */
            nodeSimplexID_t getNodeID(TriangleGeometry3::vertexidx_t _vertexIdx) const
                    { return mNodeIDs[_vertexIdx]; }
            /** 
             */
            bool hasNode(nodeSimplexID_t _nodeID) const
                    { 
                        bool found = false;
                        for (TriangleGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
                        {
                            if (getNodeID(vertexIdx) == _nodeID)
                            {
                                found = true;
                                break;
                            }
                        }

                        return found;
                    }
            /** 
             */
            edgeSimplexID_t getEdgeID(TriangleGeometry3::edgeidx_t _edgeIdx) const
                    { 
                        nodeSimplexID_t edgeNodeIDs[EdgeGeometry3::V_COUNT];
                        getEdgeNodeIDs(_edgeIdx, edgeNodeIDs);
                        return getTriComplex()->generateEdgeID(edgeNodeIDs[EdgeGeometry3::V_0], edgeNodeIDs[EdgeGeometry3::V_1]);
                    }
            /** 
             */
            void getEdgeNodeIDs(TriangleGeometry3::edgeidx_t _edgeIdx, nodeSimplexID_t (&_nodeIDs)[EdgeGeometry3::V_COUNT]) const
                    { 
                        TriangleGeometry3::vertexidx_t edgeVertexIndices[EdgeGeometry3::V_COUNT];
                        TriangleGeometry3::getEdgeVertexIndices(_edgeIdx, edgeVertexIndices);
                        for (EdgeGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
                            _nodeIDs[vertexIdx] = mNodeIDs[edgeVertexIndices[vertexIdx]];
                    }
        private:
            /**
             */
            void _attachEdge(nodeSimplexID_t _n1, nodeSimplexID_t _n2)
                    {
                        // store pointer to the parent tri complex 
                        TriComplexT * triComplex = getTriComplex();

                        // generate the edge id
                        edgeSimplexID_t edgeID = triComplex->generateEdgeID(_n1, _n2);

                        // try to find the edge in the complex
                        EdgeSimplex * edge = static_cast<EdgeSimplex*>(triComplex->simplicesReader(EdgeSimplex::ORDER).find(edgeID));

                        // if not present, create new one
                        if (edge == NULL)
                        {
                            edge = new EdgeSimplex(triComplex, _n1, _n2);
                            DYLAB_ASSERT(edge != NULL);
                        }

                        // estrablish connection with the edge
                        connect(edge);
                    }
        };
        //-----------------------------------------------------------------------------

    public:
        /// builds a map of all _tri nodes
        class TriNodes 
            : public SimplexNodes
        {
        public:
            /**
             */
            TriNodes(TriSimplex * _tri)
                : SimplexNodes(_tri)
                    { }
        };
        //-----------------------------------------------------------------------------

    public:
        /// builds a map of all _simplex edges
        class TriEdges : public SimplexEdges
        {
        public:
            TriEdges(TriSimplex * _tri)
                : SimplexEdges(_tri)
                    { }
        };
        //-----------------------------------------------------------------------------

    public:
        // only for convenience (and typing ;-)
		typedef typename TriSimplex::id_t triSimplexID_t;
        /// declare node container
        typedef HashObjContainer<triSimplexID_t, TriSimplex> TriangleSimplexMap;

    public:
        /// builds a map of all simplex triangles
        /// (i.e., 2-simplices, that are direct or undirect sub-simplices of the specified _simplex)
        class SimplexTris
        {
        private:
            ///
            Simplex * mSourceSimplex;
            TriangleSimplexMap mTris;
        public:
            SimplexTris(Simplex * _simplex)
                : mSourceSimplex(_simplex), mTris(false)
                    { 
                        DYLAB_ASSERT(mSourceSimplex != NULL);
                        // TODO
                    }
        };
        //-----------------------------------------------------------------------------

    public:
        /// merges a map of all adjacent nodes of the specified _vertexIdx, _edge or _tri
        class AdjacentNodes : public EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>::AdjacentNodes
        {
        protected:
            AdjacentNodes()
                    { }
        public:
            AdjacentNodes(NodeSimplex * _vertexIdx)
                : EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>::AdjacentNodes(_vertexIdx)
                    {  }
            AdjacentNodes(EdgeSimplex * _edge)
                : EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>::AdjacentNodes(_edge)
                    {  }
            AdjacentNodes(TriSimplex * _tri)
                    { 
                        AdjacentEdges adjacentEdges(_tri);
                        for (Simplices::Iterator it(adjacentEdges.simplicesIterator()); it.isValid(); it++)
                            mergeSimplices(it->getSubSimplices());
                    }
        };
        //-----------------------------------------------------------------------------

    public:
        /// merges a map of all adjacent edges of the specified _vertexIdx, _edge or _tri
        class AdjacentEdges : public EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>::AdjacentEdges
        {
        protected:
            AdjacentEdges()
                    { }
        public:
            AdjacentEdges(NodeSimplex * _vertexIdx)
                : EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>::AdjacentEdges(_vertexIdx)
                    {  }
            AdjacentEdges(EdgeSimplex * _edge)
                : EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>::AdjacentEdges(_edge)
                    {  }
            AdjacentEdges(TriSimplex * _tri)
                    { mergeSuperAdjacentSimplices(_tri->getSubSimplices()); }
        };
        //-----------------------------------------------------------------------------

    public:
        /// merges a map of all adjacent tris of the specified _vertexIdx, _edge or _tri
        class AdjacentTris : public AdjacentSimplices
        {
        protected:
            AdjacentTris()
                : AdjacentSimplices(TriSimplex::ORDER)
                    { }
        public:
            AdjacentTris(NodeSimplex * _vertexIdx)
                : AdjacentSimplices(TriSimplex::ORDER)
                    { mergeAdjacentSuperSimplices(_vertexIdx->getSuperSimplices()); }
            AdjacentTris(EdgeSimplex * _edge)
                : AdjacentSimplices(TriSimplex::ORDER)
                    { mergeAdjacentSuperSimplices(_edge); }
            AdjacentTris(TriSimplex * _tri)
                : AdjacentSimplices(TriSimplex::ORDER)
                    { mergeSubAdjacentSimplices(_tri); }
        };
        //-----------------------------------------------------------------------------

    public:
        /**
        */
        TriComplex()
                { }

        /**
         */           
        virtual ~TriComplex()
                { }
        
        /**
         */           
        TriSimplex * getTri(triSimplexID_t _triID)
                {  return dynamicCast<TriSimplex>(simplicesReader(TriSimplex::ORDER).get(_triID)); }

        /**
         */           
        const TriSimplex * getTri(triSimplexID_t _triID) const
                {  return dynamicCast<const TriSimplex>(simplicesReader(TriSimplex::ORDER).get(_triID)); }

        /**
         */           
        triSimplexID_t generateTriID(nodeSimplexID_t _n1, nodeSimplexID_t _n2, nodeSimplexID_t _n3) const
                { return HashGenerator<nodeSimplexID_t>::SortedAndFactorised<>().addValue(_n1, 157).addValue(_n2, 397).addValue(_n3, 1129).generate().getHash(); }
    };
    
}
//===========================================================================================
#endif // __dylab_TriComplex_h__
